import { _decorator, Animation, EventTouch, Label, Node, tween, Tween, Vec3, view } from 'cc';
import { LayerType } from '../../Basic/Config/BaseEnum';
import { PanelBase } from '../../Basic/UI/PanelBase';
import { autoComp, autoNode, rootNode } from '../../Basic/Manager/BaseManager';
import { PoolFactory } from '../../Basic/Component/PoolFactory';
import { MapItem } from './MapItem';
import { TowerItem } from './TowerItem';
import { EnemyItem } from './EnemyItem';
import { ItemData } from '../Config/GlobalModel';
import { EventManager } from '../../Basic/Manager/EventManager';
import { GameEventType } from '../Config/GameEnum';
import { Task } from '../../Util/CustomTask';
import { ButtleItem } from './ButtleItem';
import { MainModel_Ins } from './MainModel';
import { GridType } from '../../Data/Types';
import { MathUtil } from '../../Util/MathUtil';
import { DataMgr } from '../../Basic/Manager/DataManager';
import { MultiPlatform } from '../../Basic/Model/Platform/MultiPlatform';
import { RepairModel_Ins } from './RepairModel';
import { GuideModel_Ins } from './GuideModel';
import { MonsterCreateArr, MonsterCreateSpace, MonsterMaxCount } from '../Config/ConstData';

const viewSize = view.getVisibleSize();
const { ccclass, property } = _decorator;

@ccclass('MainPanel')
export class MainPanel extends PanelBase {
  panelLayer: LayerType.MainLayer = LayerType.MainLayer;
  clickBackClose: boolean = false;
  blackMask: number = 0;

  @autoComp(PoolFactory, rootNode)
  factory: PoolFactory;
  @autoNode()
  map: Node;
  @autoNode()
  wall: Node;
  @autoNode()
  tower: Node;
  @autoNode()
  enemy: Node;
  @autoNode()
  buttle: Node;
  @autoNode()
  coin: Node;

  @autoComp(Label)
  lab_coin: Label;
  @autoComp(Label)
  lab_energy: Label;
  @autoNode()
  repairDialog: Node;
  @autoNode()
  btn_upper: Node;
  @autoNode()
  btn_repair: Node;
  @autoComp(Label)
  lab_upper_cost: Label;

  selectItem: TowerItem = null;

  grid: number[][] = [];
  onRender(): void {
    MainModel_Ins.basePanel = this;
    EventManager.Ins().register(GameEventType.MAIN_CREATE_BUTTLE, this.createButtle, this);
    EventManager.Ins().register(GameEventType.MAIN_RECOVER, this.recoverItem, this);
    this.refreshUI();
    this.refreshMap();
    this.refreshMapConfig();
    this.startGame();

    const door = this.tower.children.filter((t) => t.getComponent(TowerItem).data.type === GridType.Door);
    if (!window['timeEnd']) window['timeEnd'] = 20;
    if (window['timeEnd'] != undefined) {
      door.length > 0 &&
        setTimeout(() => {
          if (GuideModel_Ins?.basePanel?.node?.parent != null) GuideModel_Ins?.basePanel?.clickClose();
          RepairModel_Ins.Show({ item: door[0].getComponent(TowerItem) });
        }, (Number(window['timeEnd']) - 3) * 1000);
      setTimeout(() => {
        this.gameLock = true;
        MultiPlatform.Ins().platformUnit.notifyMiniProgramPlayableStatus();
      }, Number(window['timeEnd']) * 1000);
    }
  }

  refreshUI() {
    this.lab_coin.string = `${MainModel_Ins.gameCoin}`;
    this.lab_energy.string = `${MainModel_Ins.gameEnergy}`;
  }
  /** 刷新地图块 */
  refreshMap() {
    const itemSpace = 100;
    const col = 7;
    const row = 13;
    const count = col * row;
    let space = count - this.map.children.length;
    while (space > 0) {
      const item = this.factory.pop('pre_map_item');
      this.map.addChild(item);
      space--;
    }

    while (space < 0) {
      this.factory.push(this.map.children[this.map.children.length - 1]);
      space++;
    }

    this.grid.length = 0;
    for (let i = 0; i < row; i++) {
      const arr = [];
      for (let j = 0; j < col; j++) {
        arr.push(0);
      }
      this.grid.push(arr);
    }

    this.map.children.forEach((child, i) => {
      let x = i % col;
      let y = Math.floor(i / col);
      child.setPosition(-((col - 1) / 2) * itemSpace + x * itemSpace, ((row - 1) / 2) * itemSpace - y * itemSpace + 75);
      const script = child.getComponent(MapItem);
      script.dataChanged();
    });
  }

  /** 读取地图配置 */
  refreshMapConfig() {
    /** 临时地图配置 */
    const wallPosArr = [
      [3, 0],
      [3, 1],
      [3, 2],
      [3, 4],
      [3, 5],
      [3, 6],
      [4, 0],
      [4, 6],
      [5, 0],
      [5, 6],
      [6, 0],
      [6, 6],
      [7, 0],
      [7, 6],
      [8, 0],
      [8, 6],
      [9, 0],
      [9, 6],
      [10, 0],
      [10, 1],
      [10, 2],
      [10, 3],
      [10, 4],
      [10, 5],
      [10, 6],
    ];
    const doorPosArr = [[3, 3]];
    const towerPosArr = [
      [4, 2],
      [4, 3],
      [4, 4],
    ];
    const other1PosArr = [[5, 3]];
    const otherPosArr = [[8, 3]];

    wallPosArr.forEach((pos, i) => {
      this.grid[pos[0]][pos[1]] = 1;
      const itemData = {
        type: GridType.GodWall,
        id: 9999,
        level: 1,
        levelMax: 1,
        hp: 999,
        hpMax: 999,
        atk: 0,
        atkRange: 0,
        atkMax: 0,
        levelCost: 0,
        coin: 0,
      };
      this.createItem('pre_tower_item', pos, itemData);
    });
    doorPosArr.forEach((pos, i) => {
      const itemData = {
        type: GridType.Door,
        id: 100,
        level: 1,
        levelMax: 3,
        hp: 100,
        hpMax: 100,
        atk: 0,
        atkRange: 0,
        atkMax: 0,
        levelCost: 50,
        coin: 0,
      };
      this.createItem('pre_tower_item', pos, itemData);
    });
    towerPosArr.forEach((pos, i) => {
      const itemData = {
        type: GridType.Tower,
        id: 1000,
        level: 1,
        levelMax: 3,
        hp: 1,
        hpMax: 1,
        atk: 10,
        atkRange: 300,
        atkMax: 10,
        levelCost: 50,
        coin: 0,
      };
      this.createItem('pre_tower_item', pos, itemData);
    });
    other1PosArr.forEach((pos, i) => {
      const itemData = {
        type: GridType.Other,
        id: 1002,
        level: 1,
        levelMax: 3,
        hp: 1,
        hpMax: 1,
        atk: 10,
        atkRange: 0,
        atkMax: 10,
        levelCost: 50,
        coin: 0,
      };
      this.createItem('pre_tower_item', pos, itemData);
    });
    otherPosArr.forEach((pos, i) => {
      const itemData = {
        type: GridType.Other,
        id: 1001,
        level: 1,
        levelMax: 3,
        hp: 1,
        hpMax: 1,
        atk: 10,
        atkRange: 0,
        atkMax: 10,
        levelCost: 50,
        coin: 0,
      };
      this.createItem('pre_tower_item', pos, itemData);
    });
  }

  createTempEnemy() {
    const enemyId = 2000;
    const tbData = DataMgr.tables.TbArchitecture.get(enemyId);

    const itemData = {
      type: GridType.Enemy,
      id: enemyId,
      level: 1,
      levelMax: tbData.attri.length,
      hp: tbData.attri[0].hp,
      hpMax: tbData.attri[0].hp,
      atk: tbData.attri[0].atk,
      atkRange: 0,
      atkMax: tbData.attri[0].atk,
      levelCost: 0,
      coin: 0,
    };

    const posArr = [
      [0, 0],
      [0, 1],
      [0, 2],
      [0, 3],
      [0, 4],
      [0, 5],
      [0, 6],
    ];
    this.createItem('pre_enemy_item', posArr[MathUtil.randomIntegerBetween(0, posArr.length)], itemData);
  }

  /** 创建子对象 */
  createItem(key: string, pos: number[], data: ItemData) {
    let parent = null;
    switch (key) {
      case 'pre_tower_item':
        parent = this.tower;
        break;
      case 'pre_enemy_item':
        parent = this.enemy;
        break;
    }
    const item = this.factory.pop(key);
    parent.addChild(item);
    item.setPosition(this.getPosVec3(pos));
    console.log(item.getPosition());

    let itemScript = null;
    switch (key) {
      case 'pre_tower_item':
        itemScript = item.getComponent(TowerItem);
        itemScript.data = data;
        itemScript.gridPos = pos;
        break;
      case 'pre_enemy_item':
        itemScript = item.getComponent(EnemyItem);
        itemScript.data = data;
        itemScript.gridPos = pos;
        break;
    }
    itemScript.dataChanged();
  }

  /** 创建子弹 */
  createButtle(_level: number, bPos: Vec3, ePos: Vec3, handler: Task.next) {
    const buttle = this.factory.pop('pre_buttle_item');
    this.buttle.addChild(buttle);
    buttle.setPosition(bPos);
    const buttleScript = buttle.getComponent(ButtleItem);
    buttleScript.data = {
      level: _level,
      ePos: ePos,
      damage: 1,
      flySpeed: 500,
      callHandler: handler,
    };
    buttleScript.dataChanged();
  }

  createFlyCoin(coin: number, bPos: Vec3) {
    const c = this.factory.pop('pre_fly_coin');
    this.buttle.addChild(c);

    const lab = c.getComponentInChildren(Label);
    lab.string = `+ ${coin}`;
    Tween.stopAllByTarget(c);
    c.setPosition(bPos);
    const ePos = bPos.clone();
    ePos.y += 100;
    tween(c)
      .to(0.5, { position: ePos })
      .call(() => {
        this.factory.push(c);
      })
      .start();
  }

  createEnemyButtle(bPos: Vec3) {
    const buttle = this.factory.pop('pre_enemy_buttle');
    this.buttle.addChild(buttle);
    buttle.setPosition(bPos);
    buttle.getComponent(Animation).on(Animation.EventType.FINISHED, () => {
      this.factory.push(buttle);
    });
  }

  gameTimer: NodeJS.Timeout = null;
  enemyCount: number = 5;
  startGame() {
    if (this.gameTimer != null) clearInterval(this.gameTimer);
    this.gameTimer = null;
    this.gameTimer = setInterval(this.updateGameTimer.bind(this), 1000);

    if (MainModel_Ins.gameGuide === 0) {
      MainModel_Ins.gameGuide++;
      const bed1 = this.tower.children.filter((t) => t.getComponent(TowerItem).data.id === 1002);
      GuideModel_Ins.Show({
        data: {
          guideNode: bed1[0],
          guidePos: bed1[0].getPosition(),
          guideHandler: () => bed1[0].getComponent(TowerItem).onClickSelf(),
        },
      });
    }
  }

  updateGameTimer() {
    if (MainModel_Ins.gameTimerLock || this.gameLock) return;
    this.enemyCount++;
    if (this.enemyCount >= MonsterCreateSpace && this.enemy.children.length < MonsterMaxCount) {
      this.enemyCount = 0;
      const count = MonsterCreateArr[MathUtil.randomIntegerBetween(0, MonsterCreateArr.length)];
      for (let i = 0; i < count; i++) {
        this.createTempEnemy();
      }
    }

    const towerArr = this.getTowerArr();
    let coin = 0;
    towerArr.forEach((tower) => {
      const _coin = tower.data.coin;
      if (_coin > 0) {
        coin += _coin;
        this.createFlyCoin(_coin, tower.node.getPosition().clone());
      }
    });

    MainModel_Ins.gameCoin += coin;
    this.refreshUI();
  }

  /**  */
  checkEnemyTarget() {
    const towerArr = [];
    this.tower.children.forEach((child) => {
      const item = child.getComponent(TowerItem);
      if (!item.IsDeah && item.data.type !== GridType.GodWall) towerArr.push(item);
    });
    this.enemy.children.forEach((child) => {
      const enemy = child.getComponent(EnemyItem);
      if (!enemy.IsDeah) enemy.checkAtkTarget(towerArr);
    });

    if (towerArr.length <= 0) {
      this.gameLock = true;
      MultiPlatform.Ins().platformUnit.notifyMiniProgramPlayableStatus();
    }
  }
  /** */
  checkTowerTarget() {
    const enemyArr = [];
    this.enemy.children.forEach((child) => {
      const enemy = child.getComponent(EnemyItem);
      if (!enemy.IsDeah) enemyArr.push(enemy);
    });
    this.tower.children.forEach((child) => {
      const item = child.getComponent(TowerItem);
      if (!item.IsDeah && item.data.type === GridType.Tower) {
        item.checkAtkTarget(enemyArr);
      }
    });
  }

  getTowerArr() {
    const towerArr: TowerItem[] = [];
    this.tower.children.forEach((child) => {
      const item = child.getComponent(TowerItem);
      if ((!item.IsDeah && item.data.type === GridType.Tower) || item.data.type === GridType.Other) towerArr.push(item);
    });
    return towerArr;
  }

  gameLock: boolean = false;

  protected update(dt: number): void {
    if (this.gameLock) return;
    this.checkTowerTarget();
    this.checkEnemyTarget();
  }

  /** 回收子对象 */
  recoverItem(item: Node) {
    this.factory.push(item);
  }

  getPosVec3(pos: number[]) {
    const itemSpace = 100;
    const col = 7;
    const row = 13;
    // const col = Math.floor(viewSize.width / itemSpace);
    // const row = Math.floor(viewSize.height / itemSpace);
    return new Vec3(-((col - 1) / 2) * itemSpace + pos[1] * itemSpace, ((row - 1) / 2) * itemSpace - pos[0] * itemSpace + 75);
  }

  /** 获取移动路径 */
  getMovePath(bPos: number[], ePos: number[]) {
    return this.aStarSearch(this.grid, bPos, ePos);
  }

  heuristic(a: number[], b: number[]): number {
    // 计算两个节点之间的曼哈顿距离
    return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]);
  }

  aStarSearch(grid: number[][], start: number[], goal: number[]): number[][] | null {
    // 定义四个方向的移动
    const directions: number[][] = [
      [0, 1],
      [1, 0],
      [0, -1],
      [-1, 0],
    ];

    // 初始化优先队列
    const openSet: [number, number[]][] = [];
    openSet.push([0, start]);

    // 初始化路径记录
    const cameFrom: { [key: string]: number[] } = {};

    // 初始化g_score和f_score
    const gScore: { [key: string]: number } = {};
    // gScore[start.toString()] = 0;
    gScore[JSON.stringify(start)] = 0;
    const fScore: { [key: string]: number } = {};
    fScore[JSON.stringify(start)] = this.heuristic(start, goal);

    while (openSet.length > 0) {
      // 取出f_score最小的节点
      openSet.sort((a, b) => a[0] - b[0]);
      const [_, current] = openSet.shift()!;

      if (current[0] === goal[0] && current[1] === goal[1]) {
        return this.reconstructPath(cameFrom, current, start);
      }

      for (const direction of directions) {
        const neighbor = [current[0] + direction[0], current[1] + direction[1]];
        if (0 <= neighbor[0] && neighbor[0] < grid.length && 0 <= neighbor[1] && neighbor[1] < grid[0].length && grid[neighbor[0]][neighbor[1]] === 0) {
          const tentativeGScore = gScore[JSON.stringify(current)]! + 1; // 假设每个移动的成本为1

          if (!gScore[JSON.stringify(neighbor)] || tentativeGScore < gScore[JSON.stringify(neighbor)]) {
            cameFrom[JSON.stringify(neighbor)] = current;
            gScore[JSON.stringify(neighbor)] = tentativeGScore;
            fScore[JSON.stringify(neighbor)] = tentativeGScore + this.heuristic(neighbor, goal);
            if (!openSet.find(([_, p]) => p[0] === neighbor[0] && p[1] === neighbor[1])) {
              openSet.push([fScore[JSON.stringify(neighbor)]!, neighbor]);
            }
          }
        }
      }
    }

    return null; // 如果没有找到路径
  }

  reconstructPath(cameFrom: { [key: string]: number[] }, current: number[], start: number[]): number[][] {
    // 从当前节点回溯到起点，构建路径
    const path: number[][] = [current];
    const key = (arr: number[]): string => JSON.stringify(arr);
    while (key(current) in cameFrom) {
      current = cameFrom[key(current)];
      if (current === start) {
        // // 防止死循环
        // console.error('路径回溯过程中检测到可能的死循环');
        break;
      } else {
        path.push(current);
      }
    }
    path.reverse();
    return path;
  }

  showDialog(item: TowerItem) {
    this.selectItem = item;
    this.btn_upper.active = this.selectItem.data.level < this.selectItem.data.levelMax;
    if (this.btn_upper.active) this.lab_upper_cost.string = `${this.selectItem.data.levelCost}`;
    this.btn_repair.active = this.selectItem.data.type === GridType.Door;
    this.repairDialog.setPosition(this.selectItem.node.getPosition());
    this.repairDialog.active = true;
  }

  itemUpper() {
    if (this.selectItem.data.level >= this.selectItem.data.levelMax) return;
    if (this.selectItem.IsDeah) return;
    const costCoin = Number(this.lab_upper_cost.string);
    if (MainModel_Ins.gameCoin < costCoin) return;

    MainModel_Ins.gameCoin -= costCoin;
    this.selectItem.upper();
    this.refreshUI();
    this.showDialog(this.selectItem);
  }

  itemRepair() {
    if (this.selectItem.IsDeah) return;
    this.selectItem.repair();
  }

  onCloseReady(): void {
    EventManager.Ins().unregister(GameEventType.MAIN_CREATE_BUTTLE, this.createButtle);
    EventManager.Ins().unregister(GameEventType.MAIN_RECOVER, this.recoverItem);
  }

  onClickBtn(t: EventTouch, param?: any) {
    const key = t.currentTarget.name.toString();
    switch (key) {
      case 'btn_close':
        this.selectItem = null;
        this.repairDialog.active = false;
        break;
      case 'btn_upper':
        this.itemUpper();
        break;
      case 'btn_repair':
        this.itemRepair();
        break;
    }
  }
}
